﻿using Microsoft.Win32;
using System;
using System.Globalization;
using System.IO;
using System.Text;

namespace Toenda.Foundation.SystemLayer {
	/// <summary>
	/// Class OS
	/// </summary>
	public static class OS {
		#region "NESTED TYPES"
		// ---------------------------------------------------
		// NESTED TYPES
		// ---------------------------------------------------
		#endregion

		#region "INTERNAL VARIABLES"
		// ---------------------------------------------------
		// INTERNAL VARIABLES
		// ---------------------------------------------------
		#endregion

		#region "CONSTRUCTORS"
		// ---------------------------------------------------
		// CONSTRUCTORS
		// ---------------------------------------------------
		#endregion

		#region "INTERFACE IMPLEMENTATIONS"
		// ---------------------------------------------------
		// INTERFACE IMPLEMENTATIONS
		// ---------------------------------------------------
		#endregion

		#region "PROPERTIES"
		// ---------------------------------------------------
		// PROPERTIES
		// ---------------------------------------------------
		
		/// <summary>
		/// Gets a value indicating whether this instance is vista or later.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is vista or later; otherwise, <c>false</c>.
		/// </value>
		public static bool IsVistaOrLater {
			get { return Environment.OSVersion.Version.Major >= 6; }
		}

		/// <summary>
		/// Gets the windows XP.
		/// </summary>
		/// <value>The windows XP.</value>
		public static Version WindowsXP {
			get { return new Version(5, 1); }
		}

		/// <summary>
		/// Gets the windows server2003.
		/// </summary>
		/// <value>The windows server2003.</value>
		public static Version WindowsServer2003 {
			get { return new Version(5, 2); }
		}

		/// <summary>
		/// Gets the windows vista.
		/// </summary>
		/// <value>The windows vista.</value>
		public static Version WindowsVista {
			get { return new Version(6, 0); }
		}

		/// <summary>
		/// Gets the revision.
		/// </summary>
		/// <value>The revision.</value>
		public static string Revision {
			get {
				NativeStructs.OSVERSIONINFOEX osviex = new NativeStructs.OSVERSIONINFOEX();
				osviex.dwOSVersionInfoSize = (uint)NativeStructs.OSVERSIONINFOEX.SizeOf;
				bool result = SafeNativeMethods.GetVersionEx(ref osviex);

				if(result) {
					return osviex.szCSDVersion;
				}
				else {
					return "Unknown";
				}
			}
		}

		/// <summary>
		/// Gets the type.
		/// </summary>
		/// <value>The type.</value>
		public static OSType Type {
			get {
				NativeStructs.OSVERSIONINFOEX osviex = new NativeStructs.OSVERSIONINFOEX();
				osviex.dwOSVersionInfoSize = (uint)NativeStructs.OSVERSIONINFOEX.SizeOf;
				bool result = SafeNativeMethods.GetVersionEx(ref osviex);
				OSType type;

				if(result) {
					if(Enum.IsDefined(typeof(OSType), (OSType)osviex.wProductType)) {
						type = (OSType)osviex.wProductType;
					}
					else {
						type = OSType.Unknown;
					}
				}
				else {
					type = OSType.Unknown;
				}

				return type;
			}
		}

		#endregion

		#region "EVENTS"
		// ---------------------------------------------------
		// EVENTS
		// ---------------------------------------------------
		#endregion

		#region "PRIVATE MEMBERS"
		// ---------------------------------------------------
		// PRIVATE MEMBERS
		// ---------------------------------------------------
		#endregion

		#region "PROTECTED MEMBERS"
		// ---------------------------------------------------
		// PROTECTED MEMBERS
		// ---------------------------------------------------
		#endregion

		#region "PUBLIC MEMBERS"
		// ---------------------------------------------------
		// PUBLIC MEMBERS
		// ---------------------------------------------------

		/// <summary>
		/// Determines whether [is dot net installed] [the specified version].
		/// </summary>
		/// <param name="version">The version.</param>
		/// <returns>
		/// 	<c>true</c> if [is dot net installed] [the specified version]; otherwise, <c>false</c>.
		/// </returns>
		public static bool IsDotNetInstalled(DotNetVersion version) {
			string systemDirectory = Environment.GetFolderPath(
				Environment.SpecialFolder.System
			);

			string netDirectory = systemDirectory.Remove(systemDirectory.LastIndexOf(Path.DirectorySeparatorChar));
			string netSubDirectoryBuild = "\\Microsoft.NET\\Framework\\v{0}.{1}.{2}";
			string netSubDirectory = "\\Microsoft.NET\\Framework\\v{0}.{1}";

			StringBuilder path = new StringBuilder();
			path.Append(netDirectory);

			switch(version) {
				default:
				case DotNetVersion.Version_1_0_3705:
					path.AppendFormat(netSubDirectoryBuild, 1, 0, 3705);
					break;

				case DotNetVersion.Version_1_1_4322:
					path.AppendFormat(netSubDirectoryBuild, 1, 1, 4322);
					break;

				case DotNetVersion.Version_2_0_50727:
					path.AppendFormat(netSubDirectoryBuild, 2, 0, 50727);
					break;

				case DotNetVersion.Version_3_0:
					path.AppendFormat(netSubDirectory, 3, 0);
					break;

				case DotNetVersion.Version_3_5:
					path.AppendFormat(netSubDirectory, 3, 5);
					break;
			}

			return Directory.Exists(path.ToString());
		}

		/// <summary>
		/// Determines whether [is dot net installed] [the specified major].
		/// </summary>
		/// <param name="major">The major.</param>
		/// <param name="minor">The minor.</param>
		/// <param name="build">The build.</param>
		/// <returns>
		/// 	<c>true</c> if [is dot net installed] [the specified major]; otherwise, <c>false</c>.
		/// </returns>
		public static bool IsDotNetInstalled(int major, int minor, int build) {
			switch(major) {
				default:
				case 1:
					switch(minor) {
						default:
						case 0:
							return IsDotNetInstalled(DotNetVersion.Version_1_0_3705);

						case 1:
							return IsDotNetInstalled(DotNetVersion.Version_1_1_4322);
					}

				case 2:
					switch(minor) {
						default:
						case 0:
							return IsDotNetInstalled(DotNetVersion.Version_2_0_50727);
					}

				case 3:
					switch(minor) {
						default:
						case 0:
							return IsDotNetInstalled(DotNetVersion.Version_3_0);

						case 5:
							return IsDotNetInstalled(DotNetVersion.Version_3_5);
					}
			}
		}

		/// <summary>
		/// Determines whether [is dot net version installed] [the specified major].
		/// </summary>
		/// <param name="major">The major.</param>
		/// <param name="minor">The minor.</param>
		/// <param name="build">The build.</param>
		/// <returns>
		/// 	<c>true</c> if [is dot net version installed] [the specified major]; otherwise, <c>false</c>.
		/// </returns>
		public static bool IsDotNetVersionInstalled(int major, int minor, int build) {
			const string regKeyNameFormat = "Software\\Microsoft\\NET Framework Setup\\NDP\\v{0}.{1}.{2}";
			const string regValueName = "Install";

			string regKeyName = string.Format(regKeyNameFormat, major.ToString(CultureInfo.InvariantCulture),
				minor.ToString(CultureInfo.InvariantCulture), build.ToString(CultureInfo.InvariantCulture));

			using(RegistryKey key = Registry.LocalMachine.OpenSubKey(regKeyName, false)) {
				object value = null;

				if(key != null) {
					value = key.GetValue(regValueName);
				}

				return (value != null && value is int && (int)value == 1);
			}
		}

		/// <summary>
		/// Checks the windows version.
		/// </summary>
		/// <param name="major">The major.</param>
		/// <param name="minor">The minor.</param>
		/// <param name="servicePack">The service pack.</param>
		/// <returns></returns>
		public static bool CheckWindowsVersion(int major, int minor, short servicePack) {
			NativeStructs.OSVERSIONINFOEX osvi = new NativeStructs.OSVERSIONINFOEX();
			osvi.dwOSVersionInfoSize = (uint)NativeStructs.OSVERSIONINFOEX.SizeOf;
			osvi.dwMajorVersion = (uint)major;
			osvi.dwMinorVersion = (uint)minor;
			osvi.wServicePackMajor = (ushort)servicePack;

			ulong mask = 0;
			mask = NativeMethods.VerSetConditionMask(mask, NativeConstants.VER_MAJORVERSION, NativeConstants.VER_GREATER_EQUAL);
			mask = NativeMethods.VerSetConditionMask(mask, NativeConstants.VER_MINORVERSION, NativeConstants.VER_GREATER_EQUAL);
			mask = NativeMethods.VerSetConditionMask(mask, NativeConstants.VER_SERVICEPACKMAJOR, NativeConstants.VER_GREATER_EQUAL);

			bool result = NativeMethods.VerifyVersionInfo(
				ref osvi,
				NativeConstants.VER_MAJORVERSION |
					NativeConstants.VER_MINORVERSION |
					NativeConstants.VER_SERVICEPACKMAJOR,
				mask);

			return result;
		}

		/// <summary>
		/// Checks the OS requirement.
		/// </summary>
		/// <returns></returns>
		public static bool CheckOSRequirement() {
			return true;
		}

		#endregion
	}
}
